package com.ly.auth.service;

import cn.hutool.core.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.util.DefaultJdbcListFactory;
import org.springframework.security.oauth2.common.util.JdbcListFactory;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

/**
 * @Author LY
 * @create 2023/6/7 11:17
 * @Description 自定义jdbc数据库读取信息
 */
@Slf4j
public class CustomJdbcClientDetailsService extends JdbcClientDetailsService {

    //操作oauth_client_details数据库SQL语句，另外可以自行注入
    private static final String CLIENT_FIELDS_FOR_UPDATE = "resource_ids, scope, "
            + "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
            + "refresh_token_validity, additional_information, autoapprove";
    private static final String CLIENT_FIELDS = "client_secret, " + CLIENT_FIELDS_FOR_UPDATE;
    private static final String BASE_FIND_STATEMENT = "select client_id, " + CLIENT_FIELDS + " from oauth_client_details";
    private static final String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id";
    private static final String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?";
        private static final String DEFAULT_INSERT_STATEMENT = "insert into oauth_client_details (" + CLIENT_FIELDS
            + ", client_id) values (?,?,?,?,?,?,?,?,?,?,?)";
    private static final String DEFAULT_UPDATE_STATEMENT = "update oauth_client_details " + "set "
            + CLIENT_FIELDS_FOR_UPDATE.replaceAll(", ", "=?, ") + "=? where client_id = ?";
    private static final String DEFAULT_UPDATE_SECRET_STATEMENT = "update oauth_client_details "
            + "set client_secret = ? where client_id = ?";
    private static final String DEFAULT_DELETE_STATEMENT = "delete from oauth_client_details where client_id = ?";

    //1.用于client_secret密码入库与出库时转化
    private PasswordEncoder passwordEncoder = NoOpPasswordEncoder.getInstance();
    //2.数据库存操作。
    private final JdbcTemplate jdbcTemplate;
    private JdbcListFactory listFactory;

    public CustomJdbcClientDetailsService(DataSource dataSource) {
        super(dataSource);
        Assert.notNull(dataSource, "DataSource required");
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.listFactory = new DefaultJdbcListFactory(new NamedParameterJdbcTemplate(jdbcTemplate));
    }

    /**
     * 核心方法。加载ClientDetails by clientId
     */
    @Override
    public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
        log.info("==== 加载ClientDetails by clientId:{} ====",clientId);
        ClientDetails details = null;
        try {
            details = jdbcTemplate.queryForObject(DEFAULT_SELECT_STATEMENT, new ClientDetailsRowMapper(), clientId);
        }
        catch (EmptyResultDataAccessException e) {
            throw new NoSuchClientException("No client with requested id: " + clientId);
        }
        return details;
    }

    static class ClientDetailsRowMapper implements RowMapper<ClientDetails> {

        @Override
        public ClientDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
            // 筛选值
            BaseClientDetails details = new BaseClientDetails();
            details.setClientId(rs.getString("client_id"));
            details.setClientSecret(rs.getString("client_secret"));
            details.setResourceIds(StringUtils.commaDelimitedListToSet(rs.getString("resource_ids")));
            details.setScope(StringUtils.commaDelimitedListToSet(rs.getString("scope")));
            details.setAuthorizedGrantTypes(StringUtils.commaDelimitedListToSet(rs.getString("authorized_grant_types")));
            details.setAuthorities(getAuthority(rs.getString("authorities")));
            return details;
        }

        private Collection<? extends GrantedAuthority> getAuthority(String authorities) {
            if (!StringUtils.hasText(authorities)) {
                return new ArrayList<>();
            }
            return AuthorityUtils.commaSeparatedStringToAuthorityList(authorities);
        }
    }
}
